import Logger from '../base/logger';
import { AMSClient, answerCall, connectAmsService, hangupCall, resumeAmsConnection } from './ams_client';
import { agentLogin, getAgentGroups, refreshNewToken } from './api';
import { releaseAmsClient, validateAmsClient, refreshAmsTicket } from './ams_client';
import { getBrowserClientInfo } from './tool';
import { CCSClient, connect, validateCcsClient, joinSession, resumeCcsConnection, quitSession, sendCustomMessage, onCustomMessage, releaseCcsClient, inviteOtherAgent, notifyTransference, onInvitationProgress, sendScanCode, sendRequestLocation, cancelInstruction, onCancelInstruction } from './ccs_client';
import { IvcsError } from '../base/error';
import { deepCopy } from './tool';
import { ivcsErrorCodeEnum } from '../base/const';
const URL = require('url');
import { cancelPeerCmd, onPeerCmdResponse, sendPeerCmd } from './peercmd';
process.env.ivcsAgentMode = 1;
class agent {
	/**
	 * 构造函数
	 */
	constructor() {
		this.ringData = {};
		this.amsClient = new AMSClient();
	}

	/**
	 * 初始化和ams建立链接
	 * @param {string} backendUrl  服务的url
	 * @param {string} name  坐席名/工号
	 * @param {string} password  密码
	 * @param {string} org 机构名
	 * @param {string} originalAmsUrl  ams服务的地址
	 * @param {string} publicKey 登录公钥
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	async init(backendUrl, name, password, org, originalAmsUrl, publicKey, onSuccess, onFailure) {
		if (backendUrl === undefined || backendUrl === '') {
			Logger.warning('backendUrl is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'backendUrl is empty');
			onFailure(error);
			return;
		}
		if (name === undefined || name === '') {
			Logger.warning('name is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'name is empty');
			onFailure(error);
			return;
		}
		if (password === undefined || password === '') {
			Logger.warning('password is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'password is empty');
			onFailure(error);
			return;
		}
		if (org === undefined || org === '') {
			Logger.warning('organization is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'organization is empty');
			onFailure(error);
			return;
		}
		try {
			let login = await agentLogin(backendUrl, name, password, org, publicKey);
			this.ccsUrl = deepCopy(backendUrl);
			this.accessToken = login.data && login.data.data && login.data.data.accessToken ? login.data.data.accessToken : '';
			let tokenData = login.data && login.data.data && login.data.data.tokenData ? login.data.data.tokenData : null;
			this.userId = tokenData.data.id;
			this.ticket = login.data && login.data.data && login.data.data.ticket ? login.data.data.ticket : '';
			this.associatedUrls = login.data && login.data.data && login.data.data.urls ? login.data.data.urls : {};
			this.domainType = this.associatedUrls.domainType ? this.associatedUrls.domainType : 'public';
			this.amsUrl = originalAmsUrl ? originalAmsUrl : this.associatedUrls.amsurl;
			const intervalTimes = 300; // default 5 minutes
			let refreshTicketTimeMs = tokenData.exp - tokenData.iat - intervalTimes > 0 ? (tokenData.exp - tokenData.iat - intervalTimes) * 1000 : intervalTimes * 1000;
			let intervalMax = Math.pow(2, 31) - 1;
			refreshTicketTimeMs > intervalMax ? (refreshTicketTimeMs = intervalMax) : undefined;
			this.refreshTokenInterval = setInterval(async () => {
				try {
					let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
					this.ticket = newTokenTicket.ticket;
					this.accessToken = newTokenTicket.accessToken;
					this.amsClient.accessToken = newTokenTicket.accessToken;
				} catch (error) {
					clearInterval(this.refreshTokenInterval);
					error.data = {
						userId: this.userId,
						ticket: this.ticket,
					};
					onFailure(error);
				}
			}, refreshTicketTimeMs);
			Logger.info(`ivcsAgent connect ams url is,${this.amsUrl}`);
			let groupList = await getAgentGroups(backendUrl, this.accessToken);
			// eslint-disable-next-line no-unused-vars
			let amsConnect = await connectAmsService(this.amsClient, this.amsUrl, this.accessToken, e => {
				onFailure(e);
				Logger.info(`ivcsAgent connect ams reconnect-error, robot name:${name}`);
			});
			onSuccess({
				token: this.accessToken,
				groupsList: groupList,
				backendUrl: this.associatedUrls.backendurl,
			});
		} catch (error) {
			Logger.info(`ivcsAgent init error,,,,${error}`);
			onFailure(error);
		}
	}

	/**
	 * 断开ams的socketio链接
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	disconnect(onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.amsClient.disconnect();
		onSuccess('ok');
	}

	/**
	 * 登入ams系统
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 * @param {function} ticketInvalidAck 刷新ticket失败
	 */
	login(onSuccess, onFailure, ticketInvalidAck) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.amsClient.login((status, resp) => {
			if (status === 'ok') {
				this.amsClient.ticket = resp.ticket;
				this.amsClient.refersh_ticket_time = resp.refersh_ticket_time;
				refreshAmsTicket(this.amsClient, this.amsClient.refersh_ticket_time, ticketInvalidAck);
				onSuccess(resp);
			} else {
				Logger.warning('you need call this method : "init"');
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 登出
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	logout(onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.logout((status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
			releaseAmsClient(this.amsClient);
			clearInterval(this.refreshTokenInterval);
		});
	}

	/**
	 * 签入技能组
	 * @param {object} groups [groups:Array(groupIds)]
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	checkIn(groups, onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.checkIn(groups, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 签出技能组
	 * @param {object} groups [groups:Array(groupIds)]
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	checkOut(groups, onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.checkOut(groups, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 坐席就绪
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	ready(onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.ready((status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 坐席就绪=>未就绪
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	unReady(onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.unReady((status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 小休
	 * @param {object} reason 小休的原因,格式:{reason:string('')}
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	otherWork(reason, onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.otherWork(reason, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 预约
	 * @param {object} data 预约的内容,格式:{appointState:enum('otherWork'|'afterWork'|'notReady'),reason:string('')}
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	appoint(reason, onSuccess, onFailure) {
		let isValid = validateAmsClient(this.amsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.amsClient.appoint(reason, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 收到振铃消息
	 * @param {function} ack 返回振铃消息内容，包含当前呼叫会话的相关信息 如:{bizScenarioId: "1",room: "",sessionId: "",url: {},userData: {service: "1", specifyAgent: "", userId: "4", callMediaType: "video", receiveMediaType: "video",}
	 */
	onRing(ack) {
		this.amsClient.onRing(data => {
			Logger.info('ivcsAgent receive ring data', data, new Date());
			this.ringData = deepCopy(data);
			let ccsUrl;
			if (this.domainType == 'public') {
				ccsUrl = URL.parse(data.url.publicUrl);
			} else if (this.domainType == 'private') {
				ccsUrl = URL.parse(data.url.privateUrl);
			} else if (this.domainType == 'local') {
				ccsUrl = URL.parse(data.url.localUrl);
			}
			this.ringData.url = URL.format(ccsUrl);
			Logger.info(`ivcsAgent agent domainType,${this.domainType},ringData with agent,${JSON.stringify(this.ringData)}`);
			ack(this.ringData);
		});
	}

	/**
	 * 收到振铃取消事件消息,一般是访客取消(退出)呼叫触发
	 * @param {function} ack 事件回调
	 */
	onCancelRing(ack) {
		this.amsClient.onStopRinging(reason => {
			this.ringData = {};
			ack(reason);
		});
	}

	/**
	 * 连接异常，被系统强制结束或者其他网络原因，触发后坐席端无法正常处理会话，需要重新初始化
	 * @param {function} ack 返回具体的异常原因
	 */
	onDrop(ack) {
		let dropMessage = {
			drop: {
				code: 401,
				message: 'connect forced termination drop',
			},
			error: {
				code: 402,
				message: 'socket io connect error',
			},
			replace: {
				code: 403,
				message: 'other person login,the current account login is invalid',
			},
			onDisconnect: {
				code: 404,
				message: 'ams socketio is disconnected',
			},
			onReconnectFailed: {
				code: 405,
				message: 'ams socketio is reconnect failed',
			},
		};
		this.amsClient.onDrop(() => {
			releaseAmsClient(this.amsClient);
			ack(dropMessage.drop);
		});
		this.amsClient.onError(async data => {
			Logger.warning('ams connect error', JSON.stringify(data), new Date());
			if (data && data.code === '1101') {
				//token expired
				let backendUrl = this.associatedUrls.backendurl;
				let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
				this.ticket = newTokenTicket.ticket;
				this.accessToken = newTokenTicket.accessToken;
				// eslint-disable-next-line no-unused-vars
				let newConnect = await connectAmsService(this.amsClient, this.amsUrl, this.accessToken);
				// eslint-disable-next-line no-unused-vars
				let resumeConnect = resumeAmsConnection(this.amsClient, this.amsClient.ticket);
			} else {
				releaseAmsClient(this.amsClient);
				ack(dropMessage.error);
			}
		});
		this.amsClient.onReplaced(() => {
			releaseAmsClient(this.amsClient);
			ack(dropMessage.replace);
		});
		this.amsClient.onDisconnect(() => {
			releaseAmsClient(this.amsClient);
			ack(dropMessage.onDisconnect);
		});
		this.amsClient.onReconnectFailed(() => {
			releaseAmsClient(this.amsClient);
			ack(dropMessage.onReconnectFailed);
		});
		this.amsClient.resumeConnectionError(reason => {
			releaseAmsClient(this.amsClient);
			ack(reason);
		});
	}

	/**
	 * 接听会话
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 * @param {function} ccsTicketInvalidAck refresh-ccs-ticket error。需要挂断会话
	 * @param {function} resumeCcsConnectionError resume-ccs-connect error。需要挂断会话
	 */
	async answer(onSuccess, onFailure, ccsTicketInvalidAck, resumeCcsConnectionError) {
		if (!this.ringData || !this.ringData.url) {
			onFailure('no ring or ring stop');
			return;
		}
		try {
			let sessionId = this.ringData.sessionId;
			let customerGroup = this.ringData.group;
			let invitationId = this.ringData.invitationId ? this.ringData.invitationId : '';
			let accessToken = this.accessToken;
			let joinData = {
				media: 'video',
				userData: {
					service: '',
					specifyAgent: '',
				},
				sessionId: sessionId,
				clientInfo: getBrowserClientInfo(),
				invitationId: invitationId,
			};
			global ? (global.ivcsSessionId = sessionId) : undefined;
			// eslint-disable-next-line no-unused-vars
			let answerCallResult = await answerCall(this.amsClient);
			this.ccsClient = new CCSClient();
			// eslint-disable-next-line no-unused-vars
			let connectCcsResult = await connect(this.ccsClient, this.ccsUrl, accessToken, connectError => {
				resumeCcsConnectionError(connectError);
				//after answer must listen onCcsDrop function,,while ccs is error;
				Logger.info(`session:${sessionId},ivcsAgent ccs sio is error,${JSON.stringify(connectError)}`);
			});
			let joinResult = await joinSession(this.ccsClient, joinData);
			//Under normal circumstances, the number of users in the current session is greater than or equal to 2
			if (joinResult.members && joinResult.members.length > 1) {
				let refreshTicketTime = joinResult.refersh_ticket_time;
				this.refreshCcsTicket(refreshTicketTime, (status, reason) => {
					if (status != 'ok') {
						Logger.info('ivcsAgent refresh ccs ticket error with sessionId=', sessionId);
						ccsTicketInvalidAck(status, reason);
					}
				});
				Logger.info('ivcsAgent answer success with sessionId=', sessionId);
				onSuccess({
					sessionId: sessionId,
					sessionData: joinResult,
					customerGroup: customerGroup,
				});
			} else {
				onFailure(new IvcsError('1017', 'there is no other user in the current session,customer has left'));
				return;
			}
		} catch (error) {
			Logger.info('ivcsAgent answer error with', error);
			onFailure(error);
		}
	}

	/**
	 * 访客视频已经推送成功
	 * @param {function} ack 事件回调
	 */
	onReadyToTalk(ack) {
		this.ccsClient.onReadyToTalk(resp => {
			ack(resp);
		});
	}

	/**
	 * 访客加入room,去拉机器人
	 * @param {function} ack 事件回调
	 */
	onCustomerJoin(ack) {
		this.ccsClient.onCustomerJoin(resp => {
			ack(resp);
		});
	}

	/**
	 * 机器人告知访客音视频已准备好
	 * @param {string} participantId participantId in room
	 * @param {object} user userInfo
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	readyToTalk(participantId, user, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.readyToTalk(participantId, user, 'customers', (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1018', resp));
			}
		});
	}

	/**
	 * 刷新ccs的socket io 链接
	 * @ignore
	 * @param refreshTicketTime
	 * @param ack
	 */
	refreshCcsTicket(refreshTicketTime, ack) {
		this.ccsClient.interval = setInterval(() => {
			this.ccsClient.refreshTicket((refreshStatus, refreshResp) => {
				if (refreshStatus === 'ok') {
					this.ccsClient.ticket = refreshResp.ticket;
					ack('ok');
				} else {
					clearInterval(this.ccsClient.interval);
					ack('error', refreshResp);
				}
			});
		}, refreshTicketTime);
	}

	/**
	 * 坐席拒绝接听
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	reject(onSuccess, onFailure) {
		this.amsClient.rejectCall(function (status, resp) {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_AMS_API_FAILED, resp));
			}
		});
	}

	/**
	 * 离开会话并挂断通话
	 * @param {object} reason 挂断原因 {reason:""}
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	hangup(reason, onSuccess, onFailure) {
		let taskList = [];
		if (this.ccsClient) {
			clearInterval(this.ccsClient.interval);
			this.ccsClient.interval = null;
		}
		if (this.amsClient && this.amsClient.accessToken) {
			taskList.push(hangupCall(this.amsClient, reason));
		}
		if (this.ccsClient && this.ccsClient.accessToken) {
			taskList.push(quitSession(reason, this.ccsClient));
		}
		Promise.all(taskList).then(
			value => {
				onSuccess(value);
			},
			error => {
				onFailure(error);
			}
		);
	}

	/**
	 * 坐席推送引导框
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	photoGuideBox(onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.photoGuideBox(false, 'customers', (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1011', resp));
			}
		});
	}

	/**
	 * ccs链接异常
	 * @param {function} ack 事件回调
	 */
	onCcsDrop(ack) {
		let dropMessage = {
			drop: {
				code: 401,
				message: 'ccs connect forced termination drop',
			},
			error: {
				code: 402,
				message: 'ccs socket io connect error',
			},
			onDisconnect: {
				code: 404,
				message: 'ccs socketio is disconnected',
			},
			onReconnectFailed: {
				code: 405,
				message: 'ccs socketio is reconnect failed',
			},
		};
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.onDrop(resp => {
			releaseCcsClient(this.ccsClient);
			ack(dropMessage.drop);
		});
		this.ccsClient.onError(async data => {
			Logger.info(`ivcaAgent ccs sio error,${JSON.stringify(data)},at ${new Date()}`);
			if (data && data.code === '1101') {
				//token expired
				let backendUrl = this.associatedUrls.backendurl;
				let newTokenTicket = await refreshNewToken(backendUrl, this.userId, this.ticket);
				this.ticket = newTokenTicket.ticket;
				this.accessToken = newTokenTicket.accessToken;
				// eslint-disable-next-line no-unused-vars
				let newConnect = await connect(this.ccsClient, this.ccsUrl, this.accessToken);
				// eslint-disable-next-line no-unused-vars
				let resumeConnect = resumeCcsConnection(this.ccsClient, this.ccsClient.ticket);
			} else {
				releaseCcsClient(this.ccsClient);
				ack(dropMessage.error);
			}
		});
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.onDisconnect(error => {
			releaseCcsClient(this.ccsClient);
			ack(dropMessage.onDisconnect);
		});
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.onReconnectFailed(error => {
			releaseCcsClient(this.ccsClient);
			ack(dropMessage.onReconnectFailed);
		});
	}

	/**
	 * 请求屏幕截图
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	requestScreenSnapshot(onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.screenSnapshot(false, 'customers', (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1012', resp));
			}
		});
	}

	/**
	 * 邀请访客手写签名
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	requestHandSignature(onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.handSign(false, 'start', 'customers', (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1013', resp));
			}
		});
	}

	/**
	 * 收到手写签名
	 * @param {function} ack 事件回调
	 */
	onHandSignature(ack) {
		this.ccsClient.onHandSignature(data => {
			ack(data);
		});
	}

	/**
	 * 坐席分享资源文件给访客
	 * @param {string} type 文件类型
	 * @param {string} url 文件url
	 * @param {string} fileName 文件名
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	shareResourceWithUrl(type, url, fileName, toRole = 'customers', onSuccess, onFailure) {
		let supportType = ['doc', 'pic', 'video', 'audio', 'page', 'paper'];
		if (supportType.indexOf(type) === -1) {
			let error = new IvcsError('2001', `type is invalid,only support 'doc','pic', 'video', 'audio', 'page', 'paper'`);
			onFailure(error);
			return;
		}
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		try {
			// eslint-disable-next-line no-unused-vars
			this.ccsClient.shareResource(type, url, fileName, toRole, (status, resp, msgId) => {
				if (status === 'ok') {
					onSuccess();
				} else {
					onFailure(resp);
				}
			});
		} catch (error) {
			onFailure(error);
		}
	}

	/**
	 * 发送文字消息
	 * @param {string} message 文字信息
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendTextMessage(message, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.sendTextMessage(message, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1014', resp));
			}
		});
	}

	/**
	 * 以特定from_clientId 发送消息
	 * @param {string} message 消息内容
	 * @param {string} from  为空，则默认是当前用户发送的消息
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendTextMessageWithFromClientId(message, from, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.sendMessageFromTo('text', message, from, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1014', resp));
			}
		});
	}

	/**
	 * 发送ASR,tts,预录制视频中的文本
	 * @param {string} message 识别的文本, 如果ai服务异常，建议发送''空字符串
	 * @param {string} type 文本类型 [ text-asr, text-tts,text-pre-recorded ]
	 * @param {string} from 为空，则默认是当前用户发送的消息
	 * @param {string} to 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败 {code,message} 统一处理成发送失败
	 */
	sendAiRecognitionMessage(message, type, from, to, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendMessageFromTo(type, message, from, to, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送AI识别的结果
	 * @param {string} message 识别的结果
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendAICheckoutMessage(message, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.sendAICheckoutMessage(message, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1014', resp));
			}
		});
	}

	/**
	 * 发送ai实时质检的结果
	 * @param {object} data
	 * @param {string} toRole  string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendRealTimeQuality(data, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.ccsClient.sendRealTimeQuality(data, toRole, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送转人工原因消息
	 * @param {object} message 转人工原因的具体消息内容 如{reason:"转人工原因"}
	 * @param {string} toRole  告知的角色，customer,agents,或者具体的clientId
	 * @param {function} onSuccess 成功回调
	 * @param {function} onFailure 失败回调
	 */
	sendTransferReasonMessage(message, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendTransferReason(message, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.error('sendTransferReason error', status, resp, msgId);
				onFailure(new IvcsError('1020', resp));
			}
		});
	}

	/**
	 * 发送会话中 变量发生变化通知
	 * @param {object} data [{id(变量id),value(变量值),roleId(角色id),roleName(角色名字)}] // 建议使用数组
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功 ok
	 * @param {function} onFailure 失败
	 */
	sendVariableChange(data, toRole = 'agents', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendVariableChange(data, toRole, (status, resp) => {
			if (status === 'ok') {
				onSuccess('ok');
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送消息确认
	 * @param {string} message 消息确认的内容
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendMessageConfirmation(message, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.sendMessageConfirmation(message, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError('1019', resp));
			}
		});
	}

	/**
	 * 收到消息确认信息
	 * @param ack
	 */
	onMessageConfirmation(ack) {
		this.ccsClient.onMessageConfirmation(data => {
			ack(data);
		});
	}

	/**
	 * 告知机器人的播收音方式
	 * @param {object} data {type:"talk-only" //talk-only | hear-only | talk-hear}
	 * @param {string} toRole {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess
	 * @param {function} onFailure
	 */
	sendCallDirection(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendCallDirection(data, toRole, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送汇总确认模版
	 * @param {object|string} data 发送汇总确认的模版的图片地址
	 * @param {string} to 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	sendSummaryConfirm(message, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendSummaryConfirm(message, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.info(`sendSummaryConfirm status,${status},resp,${resp},msgId,${msgId}`);
				onFailure(new IvcsError('1022', resp));
			}
		});
	}

	/**
	 * 接收推送的汇总模版的所有内容
	 * @param {function} ack 返回对方推送的汇总模版的详情
	 */
	onSummaryConfirm(ack) {
		this.ccsClient.onSummaryConfirm(data => {
			ack(data);
		});
	}

	/**
	 * 收到文字消息
	 * @param {function} ack 事件回调
	 */
	onTextMessage(ack) {
		this.ccsClient.onTextMessage(data => {
			ack(data);
		});
	}

	/**
	 * 收到AI识别结果
	 * @param {function} ack 事件回调
	 */
	onAICheckoutMessage(ack) {
		this.ccsClient.onAICheckoutMessage(data => {
			ack(data);
		});
	}

	/**
	 * 收到资源文件消息
	 * @param {function} ack 事件回调
	 */
	onResourceMessage(ack) {
		this.ccsClient.onSharedResource(data => {
			ack(data);
		});
	}

	/**
	 * 发送表单消息
	 * @param {object} formData 表单具体内容 example :{formId:123,fromFlowId:123123}
	 * @param {string} toRole  接受消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'customers';
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendForm(formData, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		// eslint-disable-next-line no-unused-vars
		this.ccsClient.sendForm(formData, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.info(`send form failed,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError('1015', resp));
			}
		});
	}

	/**
	 * 收到表单数据
	 * @param {function} ack 表单具体的内容
	 */
	onForm(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.warning(`onForm error,${JSON.stringify(isValid)}`);
			return;
		}
		this.ccsClient.onFormData(data => {
			ack(data);
		});
	}

	/**
	 * 发送查阅等待的消息
	 * @param {object} data 消息内容
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendWaitingRead(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.waitingRead(data, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.info(`send waiting read error,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError('1021', resp));
			}
		});
	}

	/**
	 * 监听到查阅等待的消息
	 * @param {function} ack
	 */
	onWaitingRead(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.warning(`onWaitingRead error,${JSON.stringify(isValid)}`);
			return;
		}
		this.ccsClient.onWaitingRead(data => {
			ack(data);
		});
	}

	/**
	 * 发送自定义消息
	 * @param {string} message 自定义消息的内容
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendCustomMessage(data, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		sendCustomMessage(
			this.ccsClient,
			data,
			toRole,
			resp => {
				onSuccess(resp);
			},
			error => {
				onFailure(new IvcsError('1016', error));
			}
		);
	}

	/**
	 * 收到自定义消息
	 * @param {function} ack 事件回调
	 */
	onCustomMessage(ack) {
		onCustomMessage(this.ccsClient, data => {
			ack(data);
		});
	}

	/**
	 * 收到有人离开session的消息
	 * @param {function} ack 事件回调
	 */
	onQuitSession(ack) {
		this.ccsClient.onQuitSession(data => {
			ack(data);
		});
	}

	/**
	 * 收到有人加入session的消息
	 * @param {function} ack 事件回调
	 */
	onJoinSession(ack) {
		this.ccsClient.onJoinedSession(data => {
			ack(data);
		});
	}

	/**
	 * 转接其他坐席
	 * @param {object} inviteData 转接的随路数据
	 * @param {function} inviteSuccess 邀请成功
	 * @param {function} inviteFailure 邀请失败
	 * @param {function} transferSuccess 转接成功
	 * @param {function} transferFailure 转接失败
	 */
	async transfer(inviteData, inviteSuccess, inviteFailure, transferSuccess, transferFailure) {
		if (!inviteData.service) {
			inviteData.service = '';
		}
		if (!inviteData.agentId) {
			inviteData.agentId = '*';
		}
		try {
			// eslint-disable-next-line no-unused-vars
			inviteData.status = 'start';
			await notifyTransference(this.ccsClient, inviteData);
			await inviteOtherAgent(this.ccsClient, inviteData)
				.then(resp => {
					inviteSuccess(resp);
				})
				.catch(async error => {
					inviteFailure(error);
					inviteData.status = 'invite-error';
					await notifyTransference(this.ccsClient, inviteData);
				});
			let listener = onInvitationProgress(this.ccsClient, async resp => {
				Logger.info('transfer agent progress', resp, new Date());
				let code = resp.code;
				if (code === '401' || code === '404' || code === '406' || code === '407' || code === '408' || code === '409' || code === '410' || code === '411' || code === '412' || code === '413' || code === '414' || code === '416' || code === '417' || code === '418' || code === '419' || code === '420' || code === '421' || code === '422' || code === '423') {
					inviteData.status = 'end';
					await notifyTransference(this.ccsClient, inviteData);
					transferFailure(resp);
				} else {
					try {
						if (code === '402' || code === '403' || code === '405') {
							this.ccsClient.offInvitationProgress(listener);
							// todo fix remove listener
							// 转接成功，但是无需告知转接结束。访客端根据坐席加入来处理转接已结束
							// inviteData.status = 'end';
							// await notifyTransference(this.ccsClient, inviteData);
							transferSuccess();
						}
					} catch (error) {
						inviteData.status = 'end';
						await notifyTransference(this.ccsClient, inviteData);
						transferFailure(error);
					}
				}
			});
		} catch (e) {
			inviteData.status = 'end';
			await notifyTransference(this.ccsClient, inviteData);
			inviteFailure(e);
		}
	}

	/**
	 * 转接业务场景(可指定坐席工号，可不同业务场景转接)
	 * @param {object} inviteData 相关参数，指定业务场景/坐席id {service:'123',agentId:'123456'}
	 * @param {function} onSuccess 'ok'  转接到可用坐席，坐席端自己执行挂断会话操作。
	 * @param {function} onFailure 失败 {'400':'无可用坐席','401:'转接超时',402':'其他坐席状态异常无法接听','404':'未找到坐席',
	 									'406':'转接的业务场景非工作日','407':'转接的业务场景非工作时间','408':'转接的业务场景不可用',
										'422': '转接的坐席未绑定当前业务场景对应的分组'， '423': '转接的坐席未签入当前业务场景对应的分组';
										'601':'当前转接业务已完成，暂无法转接！','602':'当前转接业务已完成，暂无法转接！','603':'当前转接业务正在进行中，暂无法转接！','604':'当前转接业务正在进行中，暂无法转接！'}
	 */
	async transferBizscenario(inviteData, onSuccess, onFailure) {
		if (!inviteData.service) {
			inviteData.service = '';
		}
		if (!inviteData.agentId) {
			inviteData.agentId = '*';
		}
		inviteData.type = 'transfer-bizscenario';
		inviteData.status = 'start';
		await notifyTransference(this.ccsClient, inviteData);
		let listener = onInvitationProgress(this.ccsClient, async resp => {
			Logger.info(`transfer agent with parameter,${inviteData},progress,${resp},time:${new Date().toISOString()}`);
			let code = resp.code;
			if (code === '401' || code === '404' || code === '406' || code === '407' || code === '408' || code === '409' || code === '410' || code === '411' || code === '412' || code === '413' || code === '414' || code === '416' || code === '417' || code === '418' || code === '419' || code === '420' || code === '421' || code === '422' || code === '423') {
				inviteData.status = 'end';
				await notifyTransference(this.ccsClient, inviteData);
				this.ccsClient.offInvitationProgress(listener);
				onFailure(resp);
			}
		});
		await inviteOtherAgent(this.ccsClient, inviteData)
			.then(async response => {
				if (response && response.hasAvailableAgent) {
					// 有可用坐席则坐席挂断
					onSuccess('ok');
				} else if (response && !response.hasAvailableAgent) {
					this.ccsClient.offInvitationProgress(listener);
					inviteData.type = 'transfer-bizscenario';
					inviteData.status = 'end';
					inviteData.code = '400';
					await notifyTransference(this.ccsClient, inviteData);
					onFailure(new IvcsError('400', 'no available agent'));
				}
			})
			.catch(async error => {
				inviteData.status = 'invite-error';
				await notifyTransference(this.ccsClient, inviteData);
				onFailure(error);
			});
	}
	/**
	 * ai模版 --- 邀请坐席
	 * @param {object} inviteData {service,agentId,callMode:'audio'|'video',microphoneStatus:'mute'|'unmute'}
	 * @param {function} inviteSuccess 邀请发送成功
	 * @param {function} inviteFailure 邀请发送失败
	 * @param {function} onSuccess 接听成功
	 * @param {function} onFailure 接听失败
	 */
	async inviteAgent(inviteData, inviteSuccess, inviteFailure, onSuccess, onFailure) {
		if (!inviteData.service) {
			inviteData.service = '';
		}
		if (!inviteData.agentId) {
			inviteData.agentId = '*';
		}
		try {
			// eslint-disable-next-line no-unused-vars
			inviteData.status = 'invite-multiple-start';
			await notifyTransference(this.ccsClient, inviteData);
			let listener = onInvitationProgress(this.ccsClient, async resp => {
				Logger.info('transfer agent progress', resp, new Date().toISOString());
				let code = resp.code;
				// 403 connect中，超时处理
				if (code === '401' || code === '404' || code === '406' || code === '407' || code === '408' || code === '409' || code === '410' || code === '411' || code === '412' || code === '413' || code === '414' || code === '416' || code === '417' || code === '418' || code === '419' || code === '420' || code === '421' || code === '422' || code === '423') {
					this.cancelInvitations(
						resp => {
							Logger.info('cancelInvitations success', resp, new Date());
						},
						error => {
							Logger.error('cancelInvitations error', error, new Date());
						}
					);
					inviteData.status = 'invite-multiple-failed';
					inviteData.result = resp;
					await notifyTransference(this.ccsClient, inviteData);
					onFailure(resp);
				} else if (code === '405') {
					inviteData.status = 'invite-multiple-success';
					await notifyTransference(this.ccsClient, inviteData);
					this.ccsClient.offInvitationProgress(listener);
					onSuccess();
				}
			});
			await inviteOtherAgent(this.ccsClient, inviteData)
				.then(async response => {
					console.log(`inviteagent success,invitedata:${inviteData},result:${response},at:${new Date().toISOString()}`);
					inviteSuccess(response);
				})
				.catch(async error => {
					console.log(`inviteagent error,invitedata:${JSON.stringify(inviteData)},result:${JSON.stringify(error)},at:${new Date().toISOString()}`);
					inviteData.status = 'invite-multiple-failed';
					inviteData.result = error;
					await notifyTransference(this.ccsClient, inviteData);
					if (error && error.code === '5006') {
						// 邀请无可用坐席
						this.ccsClient.offInvitationProgress(listener);
						onFailure(error);
					} else {
						// 针对ai模版的方案
						inviteSuccess('ok');
						//无可用坐席
						setTimeout(() => {
							onFailure(error);
						}, 1000);
					}
				});
		} catch (e) {
			console.log(`inviteagent crash error,invitedata:${inviteData},errorinfo:${e},at:${new Date().toISOString()}`);
			this.cancelInvitations(
				resp => {
					Logger.info('cancelInvitations success', resp, new Date());
				},
				error => {
					Logger.error('cancelInvitations error', error, new Date());
				}
			);
			inviteData.status = 'invite-multiple-failed';
			inviteData.result = e;
			await notifyTransference(this.ccsClient, inviteData);
			onFailure(e);
		}
	}

	/**
	 * 取消所有的invite（取消转接和三方）
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	cancelInvitations(onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
		}
		this.ccsClient.cancelInvitations(undefined, (status, resp, msgId) => {
			const data = { status: 'cancel' };
			notifyTransference(this.ccsClient, data);
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.error(`cancel invitations failed,${resp},msgId ${msgId},at:${new Date().toISOString()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 请求扫描二维码
	 * @since 1.0.4版本新增API
	 * @param {object} data 请求参数,扩展字段
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	// eslint-disable-next-line no-unused-vars
	scanCode(data = {}, toRole, onSuccess, onFailure) {
		sendScanCode(
			this.ccsClient,
			false,
			toRole,
			s => {
				onSuccess(s);
			},
			e => {
				onFailure(e);
			}
		);
	}

	/**
	 * 请求获取地理位置
	 * @since 1.0.4版本新增API
	 * @param {object} data 请求参数,扩展字段
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	// eslint-disable-next-line no-unused-vars
	requestLocation(data = {}, toRole, onSuccess, onFailure) {
		sendRequestLocation(
			this.ccsClient,
			false,
			toRole,
			s => {
				onSuccess(s);
			},
			e => {
				onFailure(e);
			}
		);
	}

	/**
	 * 发送取消事件指令
	 * @param {string|object} instructionData  instructionName 指令名 | {instructionName:'',data:''}
	 * @param {string} toRole 发送消息的用户，'customers'= 访客;'agents'= 坐席;'all'= 除了自己的所有人，默认是'agents';
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendCancelInstructionMessage(instructionData, toRole, onSuccess, onFailure) {
		cancelInstruction(
			this.ccsClient,
			instructionData,
			toRole,
			s => {
				onSuccess(s);
			},
			e => {
				onFailure(e);
			}
		);
	}

	/**
	 * 收到取消/拒绝事件的指令，如:访客取消扫码。
	 * @param {function} ack 收到取消事件指令,data.type即事件名
	 */
	onCancelInstruction(ack) {
		onCancelInstruction(this.ccsClient, data => {
			ack(data);
		});
	}

	/**
	 * 发送开启系统录制消息
	 * @param {string} recordId 开启录制返回的id
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendStartSystemRecord(recordId, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		const data = {
			recordId: recordId,
			type: 'system',
			status: 'start',
		};
		this.ccsClient.startRecording(data, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess('ok');
			} else {
				Logger.info(`send start Recording error,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送停止系统录制消息
	 * @param {string} recordId 开启录制返回的id。
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	sendStopSystemRecord(recordId, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		const data = {
			recordId: recordId,
			type: 'system',
			status: 'stop',
		};
		this.ccsClient.stopRecording(data, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess('ok');
			} else {
				Logger.info(`send stop Recording error,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送静默等待，目前适用机器人发送静默等待模版
	 * @param {object} data
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	sendSilentWaiting(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendSilentWaiting(data, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.info(`sendSilentWaiting error,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError('1023', resp));
			}
		});
	}

	/**
	 * 发送选项判断，目前适用机器人发送选项判断模版
	 * @param {object} data
	 * @param {string} toRole 发送给谁 clientId具体的用户 'customers'= 访客;'agents'= 坐席;'others'= 除了自己的所有人; 'all' = 所有人;
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	sendOptionJudgment(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendOptionJudgment(data, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				Logger.info(`sendOptionJudgment error,reason:${resp},msgId${msgId},${new Date()}`);
				onFailure(new IvcsError('1023', resp));
			}
		});
	}

	/**
	 * 推送资源（合同/网页/音频/视频/图片/其他）<AI推送查阅等待的条形码>
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 {url:string(ResourceUrl),name: string(ResourceName),type: enum('paper','page','audio','video','pic','other')},{qrcode:string(qr-code)}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendViewResource(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('view', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`sendViewResource,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消推送资源,停止查看（合同/网页/音频/视频/图片/其他）<AI推送查阅等待的条形码>
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {object} cmdData 指令相关数据
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelViewResource(cmdId, toWhom, reason, cmdData, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'view',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelViewResource,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason,
			cmdData
		);
	}

	/**
	 * 接收访客查看资源过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onViewResource(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onViewResource error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('view', resp => {
			ack(resp);
		});
	}

	/**
	 * 截图
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 默认=undefined
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	screenshot(cmdData = undefined, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('get-screenshot', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`screenshot,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消截图
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelScreenshot(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'get-screenshot',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelScreenshot,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客的截图过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onScreenShot(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onScreenShot error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('get-screenshot', resp => {
			ack(resp);
		});
	}

	/**
	 * 推送在线表单
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 {formId:string(fomId),sessionId:string(sessionId)}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendOnlineForm(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('fill-form', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`sendOnlineForm,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消访客在线表单填写
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelOnlineForm(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'fill-form',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelOnlineForm,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客填写的表单过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onFillForm(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onFillForm error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('fill-form', resp => {
			ack(resp);
		});
	}

	/**
	 * 邀请手写签名
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容,默认是undefined
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	inviteHandWriting(cmdData = undefined, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('get-handwriting', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`inviteHandWriting,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消访客手写签名
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelHandWriting(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'get-handwriting',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelHandWriting,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客签名的过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onHandWriting(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onHandWriting error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('get-handwriting', resp => {
			ack(resp);
		});
	}

	/**
	 * 请求获取访客地理位置
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 默认是undefined
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	requestCustomerLocation(cmdData = undefined, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('get-geo-location', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`requestCustomerLocation,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消获取访客地理位置
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelRequestCustomerLocation(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'get-geo-location',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelRequestCustomerLocation,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客获取地理位置的过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onGeoLocation(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onGeoLocation error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('get-geo-location', resp => {
			ack(resp);
		});
	}

	/**
	 * 请求访客扫码
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	requestCustomerScanCode(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('scan-code', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`requestCustomerScanCode,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消访客扫码
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelCustomerScanCode(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'scan-code',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelCustomerScanCode,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客扫码的过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onScanCode(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onScanCode error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('scan-code', resp => {
			ack(resp);
		});
	}

	/**
	 * 发送内容确认<目前用于AI坐席发送汇总确认消息>
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 {type: enum('online-resource','text','form','xml'...)}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendConfirm(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('confirm', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`sendConfirm,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消内容确认<目前用于AI坐席发送汇总确认消息>
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendCancelConfirm(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'confirm',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`sendCancelConfirm,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客查看内容确认过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onConfirm(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onConfirm error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('confirm', resp => {
			ack(resp);
		});
	}

	/**
	 * 发送选项判断<目前用于AI坐席发送选项判断消息>
	 * @since 1.0.5版本更新API
	 * @param {string} cmdData 指令内容 {titile: string(Titile),type: enum('simple-select'),options: array(object(Option):: {id: string(OptionId), description: string(OptionDesc)})}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	makeChoice(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('make-choice', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`makeChoice,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 取消选项判断<目前用于AI坐席发送选项判断消息>
	 * @since 1.0.5版本更新API
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason enum("normal","timeout")
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelMakeChoice(cmdId, toWhom, reason, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendPeerCancel(
			'make-choice',
			cmdId,
			toWhom,
			(status, resp) => {
				if (status === 'ok') {
					onSuccess(resp);
				} else {
					Logger.error(`cancelMakeChoice,error:${resp},at:${new Date()}`);
					onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
				}
			},
			reason
		);
	}

	/**
	 * 接收访客选择过程/结果
	 * @since 1.0.5版本更新API
	 * @param {function} ack 返回访客的响应结果
	 */
	onMakeChoice(ack) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			Logger.error('onMakeChoice error', isValid);
			return;
		}
		this.ccsClient.onPeerResponse('make-choice', resp => {
			ack(resp);
		});
	}

	/**
	 * 发送nlp模版节点内容
	 * @since 1.0.8 新增
	 * @param {object} cmdData 指令内容 {content:string()}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendNlpNode(cmdData, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		let askForPermission = false;
		this.ccsClient.sendPeerCmd('third-nlp', askForPermission, cmdData, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(resp);
			} else {
				Logger.error(`sendNlpNode,error:${resp},at:${new Date()}`);
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送AI模版节点完成情况<目前用于AI机器人完成相应节点后推送该节点的处理过程和结果给到访客>
	 * @since 1.0.6 新增
	 * @param {object} WorkflowProgressData 消息内容
	 * @param {string} from  为空，则默认是当前用户发送的消息，传system表示系统消息
	 * @param {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败 code{5005:消息发送失败}
	 */
	sendWorkflowProgress(WorkflowProgressData, from = undefined, toRole = 'customers', onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendWorkflowProgress(WorkflowProgressData, from, toRole, (status, resp, msgId) => {
			if (status === 'ok') {
				onSuccess(msgId);
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 机器人开始播报话术
	 * @param {string|object} data 播报的话术，暂定传递为空对象，{}
	 * @param {string} toRole {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess
	 * @param {function} onFailure
	 */
	sendAiStartSpeak(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendAiStartSpeak(data, toRole, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 机器人播报话术结束
	 * @param {string|object} data 播报的话术，暂定传递为空对象，{}
	 * @param {string} toRole {string} toRole 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess
	 * @param {function} onFailure
	 */
	sendAiSpeakDone(data, toRole, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendAiSpeakDone(data, toRole, (status, resp) => {
			if (status === 'ok') {
				onSuccess();
			} else {
				onFailure(new IvcsError(ivcsErrorCodeEnum.ERROR_CCS_SESSION_MESSAGE_FAILED, resp));
			}
		});
	}

	/**
	 * 发送证言抄录
	 * @param {object} cmdData 指令内容  {content:string('')}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	sendTestimonyWrite(cmdData, toWhom, onSuccess, onFailure) {
		let askForPermission = false;
		sendPeerCmd(this.ccsClient, 'testimony-write', askForPermission, cmdData, toWhom, onSuccess, onFailure);
	}

	/**
	 * 取消证言抄录
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason 取消原因 normal || timeout
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelTestimonyWrite(cmdId, toWhom, reason = 'timeout', onSuccess, onFailure) {
		cancelPeerCmd(this.ccsClient, 'testimony-write', cmdId, toWhom, onSuccess, onFailure, reason);
	}

	/**
	 * 接收访客证言抄录的过程/结果
	 * @param {function} ack 返回访客的响应结果
	 */
	onTestimonyWriteResult(ack) {
		onPeerCmdResponse(this.ccsClient, 'testimony-write', ack);
	}

	/**
	 * 采集变量
	 * @param {object} cmdData 指令内容  [{key,desc,property},{key2,desc2,property2},...]
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	collectVariable(cmdData, toWhom, onSuccess, onFailure) {
		let askForPermission = false;
		sendPeerCmd(this.ccsClient, 'collect-variable', askForPermission, cmdData, toWhom, onSuccess, onFailure);
	}

	/**
	 * 取消变量采集
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason 取消原因 normal || timeout
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelCollectVariable(cmdId, toWhom, reason = 'timeout', onSuccess, onFailure) {
		cancelPeerCmd(this.ccsClient, 'collect-variable', cmdId, toWhom, onSuccess, onFailure, reason);
	}

	/**
	 * 接收访客变量采集的过程/结果
	 * @param {function} ack 返回访客的响应结果
	 */
	onCollectVariableResult(ack) {
		onPeerCmdResponse(this.ccsClient, 'collect-variable', ack);
	}

	/**
	 * 短视频采集
	 * @param {object} cmdData 指令内容  {time:int('30')ms} 采集时长，单位ms
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	collectShortVideo(cmdData, toWhom, onSuccess, onFailure) {
		let askForPermission = false;
		sendPeerCmd(this.ccsClient, 'collect-short-video', askForPermission, cmdData, toWhom, onSuccess, onFailure);
	}

	/**
	 * 取消短视频采集
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason 取消原因 normal || timeout
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelCollectShortVideo(cmdId, toWhom, reason = 'timeout', onSuccess, onFailure) {
		cancelPeerCmd(this.ccsClient, 'collect-short-video', cmdId, toWhom, onSuccess, onFailure, reason);
	}

	/**
	 * 接收短视频采集的过程/结果
	 * @param {function} ack 返回访客的响应结果 拒绝：{reason:'',code:''} ，接受并成功拍摄：{url:string('url')}
	 */
	onCollectShortVideoResult(ack) {
		onPeerCmdResponse(this.ccsClient, 'collect-short-video', ack);
	}

	/**
	 * 发送ocr识别结果<留痕>
	 * @param {object} data ocr识别结果 {type: enum('idcard','bankcard'),result:object()}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功  'ok'
	 * @param {function} onFailure 发送失败
	 */
	sendOcrRecord(data, toWhom, onSuccess, onFailure) {
		let isValid = validateCcsClient(this.ccsClient);
		if (isValid != 'ok') {
			onFailure(isValid);
			return;
		}
		this.ccsClient.sendOcrRecord(data, toWhom, (status, resp) => {
			if (status === 'ok') {
				onSuccess(status);
			} else {
				Logger.error(`sendOcrRecord,error:${resp},at:${new Date()}`);
				dealWithCcsSendMessageResponse(status, resp, onFailure);
			}
		});
	}

	/**
	 * 采集银行卡
	 * @param {object} cmdData 指令内容 默认为{}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	collectBankCard(cmdData = {}, toWhom, onSuccess, onFailure) {
		let askForPermission = false;
		sendPeerCmd(this.ccsClient, 'bankcard-collection', askForPermission, cmdData, toWhom, onSuccess, onFailure);
	}

	/**
	 * 取消银行卡采集
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason 取消原因 normal || timeout
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelBankCardCollection(cmdId, toWhom, reason, onSuccess, onFailure) {
		cancelPeerCmd(this.ccsClient, 'bankcard-collection', cmdId, toWhom, onSuccess, onFailure, reason);
	}

	/**
	 * 接收访客银行卡采集结果
	 * @param {function} ack 返回访客的响应结果 {status:'accepted','rejected','in-progress','failed','done','canceled'. data:{urls:[]} }
	 */
	onBankCardCollectResult(ack) {
		onPeerCmdResponse(this.ccsClient, 'bankcard-collection', ack);
	}

	/**
	 * 采集身份证正反面
	 * @param {object} cmdData 指令内容 默认为{}
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {function} onSuccess 发送成功 {cmdId:string('cmdIdOfMsg'),createdAt:date()}
	 * @param {function} onFailure 发送失败
	 */
	collectIdCard(cmdData, toWhom, onSuccess, onFailure) {
		let askForPermission = false;
		sendPeerCmd(this.ccsClient, 'idcard-collection', askForPermission, cmdData, toWhom, onSuccess, onFailure);
	}

	/**
	 * 取消身份证采集
	 * @param {string} cmdId 前序发送的业务指令后返回的cmdId
	 * @param {string} toWhom 发送给谁 string(clientId) = 会话中指定的用户 'customers' = 访客;'agents' = 坐席;'others' = 除了自己的所有人; 'all' = 所有人
	 * @param {string} reason 取消原因 normal || timeout
	 * @param {function} onSuccess 发送成功
	 * @param {function} onFailure 发送失败
	 */
	cancelIdCardCollection(cmdId, toWhom, reason, onSuccess, onFailure) {
		cancelPeerCmd(this.ccsClient, 'idcard-collection', cmdId, toWhom, onSuccess, onFailure, reason);
	}

	/**
	 * 接收访客身份证采集结果
	 * @param {function} ack 返回访客的响应结果 {status:'accepted','rejected','in-progress','failed','done','canceled'. data:{urls:[]} }
	 */
	onIdCardCollectResult(ack) {
		onPeerCmdResponse(this.ccsClient, 'idcard-collection', ack);
	}

	/*******************ivcs-agent for agentZ *******************/
	/**
	 * 初始化机器人并加入指定session<本API针对agentZ模式>
	 * @param {string} backendUrl 服务url
	 * @param {string} name 坐席名/工号
	 * @param {string} password 密码
	 * @param {string} org 机构
	 * @param {string} ccsUrl ccsURL
	 * @param {string} sessionId sessionId
	 * @param {string} accessToken accessToken
	 * @param {function} onSuccess 成功
	 * @param {function} onFailure 失败
	 */
	async initAgentAndjoinSession(backendUrl, name, password, org, ccsUrl, sessionId, accessToken, onSuccess, onFailure) {
		if (backendUrl === undefined || backendUrl === '') {
			Logger.warning('backendUrl is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'backendUrl is empty');
			onFailure(error);
			return;
		}
		if (name === undefined || name === '') {
			Logger.warning('name is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'name is empty');
			onFailure(error);
			return;
		}
		if (password === undefined || password === '') {
			Logger.warning('password is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'password is empty');
			onFailure(error);
			return;
		}
		if (org === undefined || org === '') {
			Logger.warning('organization is empty');
			let error = new IvcsError(ivcsErrorCodeEnum.ERROR_PARAMETER, 'organization is empty');
			onFailure(error);
			return;
		}
		try {
			// let login = await agentLogin(backendUrl, name, password, org);
			// let accessToken = login.data && login.data.data && login.data.data.accessToken ? login.data.data.accessToken : '';
			let joinData = {
				media: 'video',
				userData: {
					service: '',
					specifyAgent: '',
				},
				sessionId: sessionId,
				clientInfo: getBrowserClientInfo(),
			};
			this.ccsClient = new CCSClient();
			// eslint-disable-next-line no-unused-vars
			let connectCcsResult = await connect(this.ccsClient, ccsUrl, accessToken, connectError => {
				Logger.info(`session:${sessionId},ivcsAgent ccs sio is error,${connectError}`);
			});
			let joinResult = await joinSession(this.ccsClient, joinData);
			Logger.info(`ivcsAgent join session ${sessionId} success,result,${joinResult},${new Date()}`);
			onSuccess({
				token: accessToken,
				sessionData: joinResult,
			});
		} catch (error) {
			onFailure(error);
		}
	}
}
export { agent };
